/*
 *
 * FileName: ZkConnection.java
 * Author:   wx:fdzhangc
 * Date:     2021/11/24 10:46
 * Description: //模块目的、功能描述
 * History: //修改记录
 * <author>      <time>      <version>    <desc>
 * 修改人姓名    修改时间    版本号       描述
 */
package com.zhangc.zcscm.zookeeper;

import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Op;
import org.apache.zookeeper.OpResult;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zhangc.zcscm.zookeeper.exception.ZkException;

/**
 * 功能描述:<br>
 *
 * @author wx:fdzhangc
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class ZkConnection implements IZkConnection {
    private static final Logger LOG = LoggerFactory.getLogger(ZkConnection.class);
    private static final int DEFAULT_SESSION_TIMEOUT = 60000;
    private static final int DEFAULT_RECONNECT_DELAY = 20000;
    private ZooKeeper _zk;
    private ReadWriteLock _zookeeperLock;
    private Lock _readLock;
    private Lock _writeLock;
    private final String _servers;
    private final int _sessionTimeOut;
    private final ZkConnection.Credentials _credentials;
    private final Random r;
    private final int maxReconnectDelay;

    public ZkConnection(String zkServers, ZkConnection.Credentials credentials) {
        this(zkServers, 60000, credentials);
    }

    public ZkConnection(String zkServers, int sessionTimeOut, ZkConnection.Credentials credentials) {
        this(zkServers, sessionTimeOut, credentials, 20000);
    }

    public ZkConnection(String zkServers, int sessionTimeOut, ZkConnection.Credentials credentials, int maxReconnectDelay) {
        this._zk = null;
        this._zookeeperLock = new ReentrantReadWriteLock();
        this._readLock = this._zookeeperLock.readLock();
        this._writeLock = this._zookeeperLock.writeLock();
        this.r = new Random(System.nanoTime());
        this._servers = zkServers;
        this._sessionTimeOut = sessionTimeOut;
        if (credentials == null) {
            throw new IllegalArgumentException("Zookeeper credentials is null!");
        } else {
            this._credentials = credentials;
            this.maxReconnectDelay = maxReconnectDelay;
        }
    }

    public void reconnect(Watcher watcher) throws InterruptedException {
        this._writeLock.lock();

        try {
            this.close();

            try {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("backoff connect");
                }

                Thread.sleep((long) this.r.nextInt(this.maxReconnectDelay));
            } catch (InterruptedException var6) {
                Thread.interrupted();
            }

            this.connect(watcher);
        } finally {
            this._writeLock.unlock();
        }

    }

    public void connect(Watcher watcher) {
        this._writeLock.lock();

        try {
            if (this._zk != null) {
                throw new IllegalStateException("zk client has already been started");
            }

            try {
                LOG.debug("Creating new ZookKeeper instance to connect to " + this._servers + ".");
                this._zk = new ZooKeeper(this._servers, this._sessionTimeOut, watcher);
                this._credentials.authenticate(this._zk);
            } catch (Exception var6) {
                throw new ZkException("Unable to connect to " + this._servers, var6);
            }
        } finally {
            this._writeLock.unlock();
        }

    }

    public void close() throws InterruptedException {
        this._writeLock.lock();

        try {
            if (this._zk != null) {
                LOG.debug("Closing ZooKeeper connected to " + this._servers);
                this._zk.close();
                this._zk = null;
            }
        } finally {
            this._writeLock.unlock();
        }

    }

    public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException {
        this._readLock.lock();

        String var4;
        try {
            var4 = this._zk.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode);
        } finally {
            this._readLock.unlock();
        }

        return var4;
    }

    public String create(String path, byte[] data, List<ACL> acl, CreateMode mode) throws KeeperException, InterruptedException {
        this._readLock.lock();

        String var5;
        try {
            var5 = this._zk.create(path, data, acl, mode);
        } finally {
            this._readLock.unlock();
        }

        return var5;
    }

    public void delete(String path) throws InterruptedException, KeeperException {
        this._readLock.lock();

        try {
            this._zk.delete(path, -1);
        } finally {
            this._readLock.unlock();
        }

    }

    public void delete(String path, int version) throws InterruptedException, KeeperException {
        this._readLock.lock();

        try {
            this._zk.delete(path, version);
        } finally {
            this._readLock.unlock();
        }

    }

    public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException {
        this._readLock.lock();

        boolean var3;
        try {
            var3 = this._zk.exists(path, watch) != null;
        } finally {
            this._readLock.unlock();
        }

        return var3;
    }

    public List<String> getChildren(String path, boolean watch) throws KeeperException, InterruptedException {
        this._readLock.lock();

        List var3;
        try {
            var3 = this._zk.getChildren(path, watch);
        } finally {
            this._readLock.unlock();
        }

        return var3;
    }

    public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException {
        this._readLock.lock();

        byte[] var4;
        try {
            var4 = this._zk.getData(path, watch, stat);
        } finally {
            this._readLock.unlock();
        }

        return var4;
    }

    public void writeData(String path, byte[] data) throws KeeperException, InterruptedException {
        this.writeData(path, data, -1);
    }

    public void writeData(String path, byte[] data, int version) throws KeeperException, InterruptedException {
        this._readLock.lock();

        try {
            this._zk.setData(path, data, version);
        } finally {
            this._readLock.unlock();
        }

    }

    public Stat writeDataReturnStat(String path, byte[] data, int version) throws KeeperException, InterruptedException {
        this._readLock.lock();

        Stat var4;
        try {
            var4 = this._zk.setData(path, data, version);
        } finally {
            this._readLock.unlock();
        }

        return var4;
    }

    public ZooKeeper.States getZookeeperState() {
        this._readLock.lock();

        ZooKeeper.States var1;
        try {
            var1 = this._zk != null ? this._zk.getState() : null;
        } finally {
            this._readLock.unlock();
        }

        return var1;
    }

    public ZooKeeper getZookeeper() {
        this._readLock.lock();

        ZooKeeper var1;
        try {
            var1 = this._zk;
        } finally {
            this._readLock.unlock();
        }

        return var1;
    }

    public long getCreateTime(String path) throws KeeperException, InterruptedException {
        this._readLock.lock();

        long var3;
        try {
            Stat stat = this._zk.exists(path, false);
            if (stat == null) {
                var3 = -1L;
                return var3;
            }

            var3 = stat.getCtime();
        } finally {
            this._readLock.unlock();
        }

        return var3;
    }

    public String getServers() {
        return this._servers;
    }

    public List<OpResult> multi(Iterable<Op> ops) throws KeeperException, InterruptedException {
        this._readLock.lock();

        List var2;
        try {
            var2 = this._zk.multi(ops);
        } finally {
            this._readLock.unlock();
        }

        return var2;
    }

    public void setAcl(String path, List<ACL> acl, int version) throws KeeperException, InterruptedException {
        this._readLock.lock();

        try {
            this._zk.setACL(path, acl, version);
        } finally {
            this._readLock.unlock();
        }

    }

    public Map.Entry<List<ACL>, Stat> getAcl(String path) throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        this._readLock.lock();

        AbstractMap.SimpleEntry var4;
        try {
            List<ACL> acl = this._zk.getACL(path, stat);
            var4 = new AbstractMap.SimpleEntry(acl, stat);
        } finally {
            this._readLock.unlock();
        }

        return var4;
    }

    public long getSessionId() {
        this._readLock.lock();

        long var1;
        try {
            var1 = this._zk.getSessionId();
        } finally {
            this._readLock.unlock();
        }

        return var1;
    }

    public static ZkConnection.Credentials digestCredentials(String username, String password) {
        return credentials("digest", (username + ":" + password).getBytes());
    }

    public static ZkConnection.Credentials credentials(final String scheme, final byte[] authToken) {
        return new ZkConnection.Credentials() {
            public void authenticate(ZooKeeper zooKeeper) {
                zooKeeper.addAuthInfo(scheme, authToken);
            }
        };
    }

    public interface Credentials {
        ZkConnection.Credentials NONE = new ZkConnection.Credentials() {
            public void authenticate(ZooKeeper zooKeeper) {
            }
        };

        void authenticate(ZooKeeper var1);
    }
}
